//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

#include <algorithm>
#include "stdafx.h"
#include "D3D12HelloTriangle.h"
#include "imgui.h"
#include "imgui_impl_win32.h"
#include "imgui_impl_dx12.h"
#include <sstream>
#include <fstream>
#include <iomanip>
#include "dxcapi.h"

ComPtr<ID3D12DebugDevice> DebugDevice;

D3D12HelloTexture::D3D12HelloTexture(UINT width, UINT height, std::wstring name) :
    DXSample(width, height, name),
    m_frameIndex(0),
    m_viewport(0.0f, 0.0f, static_cast<float>(width), static_cast<float>(height)),
    m_scissorRect(0, 0, static_cast<LONG>(width), static_cast<LONG>(height)),
    m_rtvDescriptorSize(0)
{
}

void D3D12HelloTexture::OnInit()
{
    LoadPipeline();
    LoadAssets();

    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls

    // Setup Dear ImGui style
    ImGui::StyleColorsDark();
    //ImGui::StyleColorsLight();

    // Setup Platform/Renderer backends
    ImGui_ImplWin32_Init(Win32Application::GetHwnd());
    ImGui_ImplDX12_Init(m_device.Get(), FrameCount,
        DXGI_FORMAT_R8G8B8A8_UNORM, m_imguiSrvHeap.Get(),
        m_imguiSrvHeap->GetCPUDescriptorHandleForHeapStart(),
        m_imguiSrvHeap->GetGPUDescriptorHandleForHeapStart());

    // Load Fonts
    // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
    // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
    // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
    // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
    // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
    // - Read 'docs/FONTS.md' for more instructions and details.
    // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
    //io.Fonts->AddFontDefault();
    //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
    //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
    //IM_ASSERT(font != nullptr);
}

// Load the rendering pipeline dependencies.
void D3D12HelloTexture::LoadPipeline()
{
    UINT dxgiFactoryFlags = 0;

#if defined(_DEBUG)
    // Enable the debug layer (requires the Graphics Tools "optional feature").
    // NOTE: Enabling the debug layer after device creation will invalidate the active device.
    {
        ComPtr<ID3D12Debug> debugController;
        ComPtr<ID3D12Debug1> debugController1;
        if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
        {
            if (SUCCEEDED(debugController->QueryInterface(IID_PPV_ARGS(&debugController1))))
            {
                debugController1->SetEnableGPUBasedValidation(true);
            }
            debugController->EnableDebugLayer();

            // Enable additional debug layers.
            dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
        }
    }
#endif

    ComPtr<IDXGIFactory4> factory;
    ThrowIfFailed(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));

    if (m_useWarpDevice)
    {
        ComPtr<IDXGIAdapter> warpAdapter;
        ThrowIfFailed(factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)));

        ThrowIfFailed(D3D12CreateDevice(
            warpAdapter.Get(),
            D3D_FEATURE_LEVEL_12_2,
            IID_PPV_ARGS(&m_device)
            ));
    }
    else
    {
        ComPtr<IDXGIAdapter1> hardwareAdapter;
        GetHardwareAdapter(factory.Get(), &hardwareAdapter);

        ThrowIfFailed(D3D12CreateDevice(
            hardwareAdapter.Get(),
            D3D_FEATURE_LEVEL_12_2,
            IID_PPV_ARGS(&m_device)
            ));
    }

    // Describe and create the command queue.
    D3D12_COMMAND_QUEUE_DESC queueDesc = {};
    queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;

    ThrowIfFailed(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue)));

    // Describe and create the swap chain.
    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
    swapChainDesc.BufferCount = FrameCount;
    swapChainDesc.Width = m_width;
    swapChainDesc.Height = m_height;
    swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
    swapChainDesc.SampleDesc.Count = 1;

    ComPtr<IDXGISwapChain1> swapChain;
    ThrowIfFailed(factory->CreateSwapChainForHwnd(
        m_commandQueue.Get(),        // Swap chain needs the queue so that it can force a flush on it.
        Win32Application::GetHwnd(),
        &swapChainDesc,
        nullptr,
        nullptr,
        &swapChain
        ));

    // This sample does not support fullscreen transitions.
    ThrowIfFailed(factory->MakeWindowAssociation(Win32Application::GetHwnd(), DXGI_MWA_NO_ALT_ENTER));

    ThrowIfFailed(swapChain.As(&m_swapChain));
    m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();

    // Create descriptor heaps.
    {
        // Describe and create a render target view (RTV) descriptor heap.
        D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
        rtvHeapDesc.NumDescriptors = FrameCount;
        rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
        rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
        ThrowIfFailed(m_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap)));

        // Describe and create a shader resource view (SRV) heap for the texture.
        D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
        srvHeapDesc.NumDescriptors = 2;
        srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
        srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
        ThrowIfFailed(m_device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&m_srvHeap)));

        // Describe and create a shader resource view (SRV) heap for the texture.
        //D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
        srvHeapDesc.NumDescriptors = 1;
        srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
        srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
        //srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
        ThrowIfFailed(m_device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&m_feedbackHeap)));

        // Describe and create a shader resource view (SRV) heap for the texture.
        //D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
        srvHeapDesc.NumDescriptors = 1;
        srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
        srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
        ThrowIfFailed(m_device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&m_imguiSrvHeap)));

        m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
        m_srvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    }

    // Create frame resources.
    {
        CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart());

        // Create a RTV for each frame.
        for (UINT n = 0; n < FrameCount; n++)
        {
            ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n])));
            m_device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle);
            rtvHandle.Offset(1, m_rtvDescriptorSize);
        }
    }

    ThrowIfFailed(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator)));
}

void OutputBlob(IDxcBlobEncoding* pBlob)
{
    BOOL known = FALSE;
    UINT32 codePage = 0;
    if (SUCCEEDED(pBlob->GetEncoding(&known, &codePage)))
    {
        if (known && codePage == CP_UTF8)
        {
            // Blob 是 UTF-8 编码的，我们可以直接输出
            const char* text = (const char*)pBlob->GetBufferPointer();
            size_t size = pBlob->GetBufferSize();
            std::string str(text, text + size);
            OutputDebugStringA(str.c_str());
        }
        else
        {
            // Blob 不是 UTF-8 编码的，需要进行转换
            // 这里只是一个示例，实际情况可能需要更复杂的处理
            OutputDebugStringA("Blob is not UTF-8 encoded.\n");
        }
    }
    else
    {
        OutputDebugStringA("Failed to get encoding of Blob.\n");
    }
}

// Load the sample assets.
void D3D12HelloTexture::LoadAssets()
{
    // Create the root signature.
    {
        D3D12_FEATURE_DATA_ROOT_SIGNATURE featureData = {};

        // This is the highest version the sample supports. If CheckFeatureSupport succeeds, the HighestVersion returned will not be greater than this.
        featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1;

        if (FAILED(m_device->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, &featureData, sizeof(featureData))))
        {
            featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0;
        }

        CD3DX12_DESCRIPTOR_RANGE1 ranges[2];
        ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC);

        CD3DX12_ROOT_PARAMETER1 rootParameters[1];
        rootParameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);

        D3D12_STATIC_SAMPLER_DESC sampler = {};
        sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
        sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
        sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
        sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
        sampler.MipLODBias = 0;
        sampler.MaxAnisotropy = 0;
        sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
        sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
        sampler.MinLOD = 0.0f;
        sampler.MaxLOD = D3D12_FLOAT32_MAX;
        sampler.ShaderRegister = 0;
        sampler.RegisterSpace = 0;
        sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

        CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc;
        rootSignatureDesc.Init_1_1(_countof(rootParameters), rootParameters, 1, &sampler, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

        ComPtr<ID3DBlob> signature;
        ComPtr<ID3DBlob> error;
        ThrowIfFailed(D3DX12SerializeVersionedRootSignature(&rootSignatureDesc, featureData.HighestVersion, &signature, &error));
        ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)));

        // root sig with feedback map bound
        {
            // add a UAV for the feedback map
            //ranges.push_back(CD3DX12_DESCRIPTOR_RANGE1(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE,
            //    UINT(Descriptors::HeapOffsetFeedback)));

            ranges[1] = CD3DX12_DESCRIPTOR_RANGE1(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE, 1);
                //UINT(Descriptors::HeapOffsetFeedback))
                //.InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);

            // add uav range to previous root param
            //rootParam.InitAsDescriptorTable((UINT)ranges.size(), ranges.data(), D3D12_SHADER_VISIBILITY_PIXEL);
            //rootParameters[(UINT)RootSigParams::ParamObjectTextures] = rootParam;

            rootParameters[0].InitAsDescriptorTable(2, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);

            // re-serialize
            CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc;
            rootSignatureDesc.Init_1_1((UINT)1, &rootParameters[0], 1, &sampler, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

            ComPtr<ID3DBlob> signature;
            ComPtr<ID3DBlob> error;
            ThrowIfFailed(D3D12SerializeVersionedRootSignature(&rootSignatureDesc, &signature, &error));
            ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignatureFB)));
        }

    }

    // Create the pipeline state, which includes compiling and loading shaders.
    {
        std::vector<LPCWSTR> arguments;// options = {};
        //options.DebugInfo = true; // 相当于 -Zi
        //options.OptimizationLevel = DxcOptimizationLevel::Level0; // 相当于 -Od
#if defined(_DEBUG)
        // Enable better shader debugging with the graphics debugging tools.
        UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
        arguments.push_back(L"-Zi");
        arguments.push_back(L"-Od");
#else
        UINT compileFlags = 0;
#endif
        ComPtr<ID3DBlob> error;
        //HRESULT hr = D3DCompileFromFile(GetAssetFullPath(L"shaders.hlsl").c_str(), nullptr, nullptr, "VSMain", "vs_6_0", compileFlags, 0, &vertexShader, &error);
        //if (FAILED(hr) && error.Get() != nullptr)
        //{
        //    std::wstringstream sstr;
        //    sstr << static_cast<const char*>(error->GetBufferPointer()) << "\n";
        //    std::wstring msg = sstr.str();
        //    OutputDebugStringW(msg.c_str());

        //}
        //ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"shaders.hlsl").c_str(), nullptr, nullptr, "PSMainFB", "ps_6_0", compileFlags, 0, &pixelShader, nullptr));

        ComPtr<IDxcLibrary> pLibrary;
        ComPtr<IDxcCompiler> pCompiler;
        ComPtr<IDxcBlobEncoding> pSource;

        ComPtr<IDxcBlob> vertexShader;
        ComPtr<IDxcBlob> pixelShader;
        ComPtr<IDxcBlob> pixelShaderFB;
        ComPtr<IDxcBlob> computeShader;

        // 初始化 dxc
        DxcCreateInstance(CLSID_DxcLibrary, __uuidof(IDxcLibrary), (void**)&pLibrary);
        DxcCreateInstance(CLSID_DxcCompiler, __uuidof(IDxcCompiler), (void**)&pCompiler);

        // 从文件中读取 HLSL 代码
        std::ifstream hlslFile(GetAssetFullPath(L"shaders.hlsl"));
        if (hlslFile.fail())
        {
            OutputDebugStringA("Error: Failed to open file 'shaders.hlsl'\n");
            ThrowIfFailed(E_FAIL); // 或者你可以选择其他的错误处理方式
        }
        std::string hlslCode((std::istreambuf_iterator<char>(hlslFile)), std::istreambuf_iterator<char>());
        pLibrary->CreateBlobWithEncodingFromPinned(hlslCode.c_str(), hlslCode.size(), 0, &pSource);

        // 编译 HLSL 代码
        ComPtr<IDxcOperationResult> pResult;
        pCompiler->Compile(pSource.Get(), L"shaders.hlsl", L"VSMain", L"vs_6_5", arguments.data(), (UINT32)arguments.size(), nullptr, 0, nullptr, &pResult);

        // 检查编译结果
        HRESULT hrCompilation;
        pResult->GetStatus(&hrCompilation);
        if (SUCCEEDED(hrCompilation)) {
            // 编译成功，获取编译后的字节码
            pResult->GetResult(&vertexShader);
        }
        else {
            // 编译失败，获取错误信息
            IDxcBlobEncoding* pError;
            pResult->GetErrorBuffer(&pError);
            OutputBlob(pError);
            ThrowIfFailed(E_FAIL);
        }

        pCompiler->Compile(pSource.Get(), L"shaders.hlsl", L"PSMain", L"ps_6_5", arguments.data(), (UINT32)arguments.size(), nullptr, 0, nullptr, &pResult);

        // 检查编译结果
        pResult->GetStatus(&hrCompilation);
        if (SUCCEEDED(hrCompilation)) {
            // 编译成功，获取编译后的字节码
            pResult->GetResult(&pixelShader);
        }
        else {
            // 编译失败，获取错误信息
            IDxcBlobEncoding* pError;
            pResult->GetErrorBuffer(&pError);
            OutputBlob(pError);
            ThrowIfFailed(E_FAIL);
        }

        pCompiler->Compile(pSource.Get(), L"shaders.hlsl", L"PSMainFB", L"ps_6_5", arguments.data(), (UINT32)arguments.size(), nullptr, 0, nullptr, &pResult);

        // 检查编译结果
        pResult->GetStatus(&hrCompilation);
        if (SUCCEEDED(hrCompilation)) {
            // 编译成功，获取编译后的字节码
            pResult->GetResult(&pixelShaderFB);
        }
        else {
            // 编译失败，获取错误信息
            IDxcBlobEncoding* pError;
            pResult->GetErrorBuffer(&pError);
            OutputBlob(pError);
            ThrowIfFailed(E_FAIL);
        }

        // 从文件中读取 HLSL 代码
        std::ifstream mipmapsFile(GetAssetFullPath(L"mipmaps.hlsl"));
        if (mipmapsFile.fail())
        {
            OutputDebugStringA("Error: Failed to open file 'mipmaps.hlsl'\n");
            ThrowIfFailed(E_FAIL); // 或者你可以选择其他的错误处理方式
        }
        std::string mipmapsCode((std::istreambuf_iterator<char>(mipmapsFile)), std::istreambuf_iterator<char>());
        pLibrary->CreateBlobWithEncodingFromPinned(mipmapsCode.c_str(), mipmapsCode.size(), 0, &pSource);

        // 编译 HLSL 代码
        //ComPtr<IDxcOperationResult> pResult;
        pCompiler->Compile(pSource.Get(), L"mipmaps.hlsl", L"GenerateMipMaps", L"cs_6_5", arguments.data(), (UINT32)arguments.size(), nullptr, 0, nullptr, &pResult);

        // 检查编译结果
        //HRESULT hrCompilation;
        pResult->GetStatus(&hrCompilation);
        if (SUCCEEDED(hrCompilation)) {
            // 编译成功，获取编译后的字节码
            pResult->GetResult(&computeShader);
        }
        else {
            // 编译失败，获取错误信息
            IDxcBlobEncoding* pError;
            pResult->GetErrorBuffer(&pError);
            OutputBlob(pError);
            ThrowIfFailed(E_FAIL);
        }

        // 释放资源
        pSource.Reset();
        pCompiler.Reset();
        pLibrary.Reset();

        // Define the vertex input layout.
        D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
        {
            { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
            { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
        };

        // Describe and create the graphics pipeline state object (PSO).
        D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
        psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) };
        psoDesc.pRootSignature = m_rootSignatureFB.Get();
        psoDesc.VS.pShaderBytecode = vertexShader->GetBufferPointer();
        psoDesc.VS.BytecodeLength = vertexShader->GetBufferSize();
        psoDesc.PS.pShaderBytecode = pixelShaderFB->GetBufferPointer();
        psoDesc.PS.BytecodeLength = pixelShaderFB->GetBufferSize();
        psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
        psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
        psoDesc.DepthStencilState.DepthEnable = FALSE;
        psoDesc.DepthStencilState.StencilEnable = FALSE;
        psoDesc.SampleMask = UINT_MAX;
        psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
        psoDesc.NumRenderTargets = 1;
        psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
        psoDesc.SampleDesc.Count = 1;
        ThrowIfFailed(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineStateFB)));

        psoDesc.PS.pShaderBytecode = pixelShader->GetBufferPointer();
        psoDesc.PS.BytecodeLength = pixelShader->GetBufferSize();
        ThrowIfFailed(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineState)));
    }

    // Create the command list.
    ThrowIfFailed(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator.Get(), nullptr, IID_PPV_ARGS(&m_commandList)));

    // Create the vertex buffer.
    {
        // Define the geometry for a triangle.
        Vertex triangleVertices[90] =
        {
            // bottom-left triangle
            { { -0.8f, 0.8f * m_aspectRatio, 0.0f }, { 0.0f, 0.0f } },
            { { 0.8f, -0.8f * m_aspectRatio, 0.0f }, { 1.0f, 1.0f } },
            { { -0.8f, -0.8f * m_aspectRatio, 0.0f }, { 0.0f, 1.0f } },

            // top-right triangle
            { { -0.8f, 0.8f * m_aspectRatio, 0.0f }, { 0.0f, 0.0f } },
            { { 0.8f, 0.8f * m_aspectRatio, 0.0f }, { 1.0f, 0.0f } },
            { { 0.8f, -0.8f * m_aspectRatio, 0.0f }, { 1.0f, 1.0f } },
        };

        {
            const Vertex* pVertex = &triangleVertices[0];
            const int start_group = 1;
            for (int i = 0; i < 10; i++)
            {
                for (int j = 0; j < 6; j++)
                {
                    auto& vertex = triangleVertices[(i + start_group) * 6 + j];
                    vertex.position = (pVertex + j)->position;
                    vertex.uv.x = (pVertex + j)->uv.x * (1 << (i + 1)); // x
                    vertex.uv.y = (pVertex + j)->uv.y * (1 << (i + 1)); // y
                }
            }
        }

        {
            float offset[4][2]{
                // offset: x, y
                {0.0f, 0.0f}, // top left
                {0.5f, 0.0f}, // top right
                {0.0f, 0.5f}, // bottom left
                {0.5f, 0.5f}, // bottom right

            };
            const Vertex* pVertex = &triangleVertices[0];
            const int start_group = 11;
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 6; j++)
                {
                    triangleVertices[(i + start_group) * 6 + j].position = (pVertex+j)->position;
                    triangleVertices[(i + start_group) * 6 + j].uv.x = (pVertex + j)->uv.x / 2.0f + offset[i][0]; // x
                    triangleVertices[(i + start_group) * 6 + j].uv.y = (pVertex + j)->uv.y / 2.0f + offset[i][1]; // y
                }
            }
        }

        memcpy(m_triangleVertices, triangleVertices, sizeof(triangleVertices));

        const UINT vertexBufferSize = sizeof(triangleVertices);

        // Note: using upload heaps to transfer static data like vert buffers is not 
        // recommended. Every time the GPU needs it, the upload heap will be marshalled 
        // over. Please read up on Default Heap usage. An upload heap is used here for 
        // code simplicity and because there are very few verts to actually transfer.
        ThrowIfFailed(m_device->CreateCommittedResource(
            &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
            D3D12_HEAP_FLAG_NONE,
            &CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize),
            D3D12_RESOURCE_STATE_GENERIC_READ,
            nullptr,
            IID_PPV_ARGS(&m_vertexBuffer)));

        // Copy the triangle data to the vertex buffer.
        UINT8* pVertexDataBegin;
        CD3DX12_RANGE readRange(0, 0);        // We do not intend to read from this resource on the CPU.
        ThrowIfFailed(m_vertexBuffer->Map(0, &readRange, reinterpret_cast<void**>(&pVertexDataBegin)));
        memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices));
        m_vertexBuffer->Unmap(0, nullptr);

        // Initialize the vertex buffer view.
        m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress();
        m_vertexBufferView.StrideInBytes = sizeof(Vertex);
        m_vertexBufferView.SizeInBytes = vertexBufferSize;
    }

    // Note: ComPtr's are CPU objects but this resource needs to stay in scope until
    // the command list that references it has finished executing on the GPU.
    // We will flush the GPU at the end of this method to ensure the resource is not
    // prematurely destroyed.
    ComPtr<ID3D12Resource> textureUploadHeap;

    // Create the texture.
    {
        // Describe and create a Texture2D.
        D3D12_RESOURCE_DESC textureDesc = {};
        textureDesc.MipLevels = 1;
        textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        textureDesc.Width = TextureWidth;
        textureDesc.Height = TextureHeight;
        textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
        textureDesc.DepthOrArraySize = 1;
        textureDesc.MipLevels = 11;
        textureDesc.SampleDesc.Count = 1;
        textureDesc.SampleDesc.Quality = 0;
        textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;

        ThrowIfFailed(m_device->CreateCommittedResource(
            &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
            D3D12_HEAP_FLAG_NONE,
            &textureDesc,
            D3D12_RESOURCE_STATE_COPY_DEST,
            nullptr,
            IID_PPV_ARGS(&m_texture)));

        const UINT64 uploadBufferSize = GetRequiredIntermediateSize(m_texture.Get(), 0, 1);

        // Create the GPU upload buffer.
        ThrowIfFailed(m_device->CreateCommittedResource(
            &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
            D3D12_HEAP_FLAG_NONE,
            &CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize),
            D3D12_RESOURCE_STATE_GENERIC_READ,
            nullptr,
            IID_PPV_ARGS(&textureUploadHeap)));

        // Copy data to the intermediate upload heap and then schedule a copy 
        // from the upload heap to the Texture2D.
        std::vector<UINT8> texture = GenerateTextureData();

        D3D12_SUBRESOURCE_DATA textureData = {};
        textureData.pData = &texture[0];
        textureData.RowPitch = TextureWidth * TexturePixelSize;
        textureData.SlicePitch = textureData.RowPitch * TextureHeight;

        UpdateSubresources(m_commandList.Get(), m_texture.Get(), textureUploadHeap.Get(), 0, 0, 1, &textureData);
        m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));

        // Describe and create a SRV for the texture.
        D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
        srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
        srvDesc.Format = textureDesc.Format;
        srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
        srvDesc.Texture2D.MipLevels = 11;
        m_device->CreateShaderResourceView(m_texture.Get(), &srvDesc, m_srvHeap->GetCPUDescriptorHandleForHeapStart());
    }

    // create the feedback map
    // the dimensions of the feedback map must match the size of the streaming texture
    {
        auto desc = m_texture->GetDesc();
        D3D12_RESOURCE_DESC1 sfbDesc = CD3DX12_RESOURCE_DESC1::Tex2D(
            configs[m_mode].Format,
            //DXGI_FORMAT_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE,
            //DXGI_FORMAT_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE,
            desc.Width, desc.Height, desc.DepthOrArraySize, desc.MipLevels);
        sfbDesc.SamplerFeedbackMipRegion = D3D12_MIP_REGION{
            128, 128, 1 };

        // the feedback texture must be in the unordered state to be written, then transitioned to RESOLVE_SOURCE
        sfbDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;

        const auto heapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
        ComPtr<ID3D12Device8> device8;
        ThrowIfFailed(m_device.As(&device8));
        ThrowIfFailed(device8->CreateCommittedResource2(
            &heapProperties, D3D12_HEAP_FLAG_NONE,
            &sfbDesc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
            nullptr, // not a render target, so optimized clear value illegal. That's ok, clear value is ignored on feedback maps
            nullptr, IID_PPV_ARGS(&m_feedbackResource)));
        m_feedbackResource->SetName(L"m_feedbackResource");

        {
            D3D12_RESOURCE_DESC desc;
            desc = m_feedbackResource->GetDesc();
            std::stringstream ss;
            ss << "MipLevels: " << desc.MipLevels;
            OutputDebugStringA(ss.str().c_str());
        }

        CD3DX12_CPU_DESCRIPTOR_HANDLE feedbackHandle(m_feedbackHeap->GetCPUDescriptorHandleForHeapStart());// , 1, m_srvDescriptorSize);
        device8->CreateSamplerFeedbackUnorderedAccessView(
            m_texture.Get(),
            m_feedbackResource.Get(),
            feedbackHandle);
        {
            CD3DX12_CPU_DESCRIPTOR_HANDLE srvHandle(m_srvHeap->GetCPUDescriptorHandleForHeapStart(), 1, m_srvDescriptorSize);
            m_device->CopyDescriptorsSimple(1, srvHandle, m_feedbackHeap->GetCPUDescriptorHandleForHeapStart(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
        }
    }

    // create gpu-side resolve destination
    {
        const auto heapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);

        const auto textureDesc = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R8_UINT, m_numTilesWidth, m_numTilesHeight, 1, /*11*/configs[m_mode].MipLevels);

        ThrowIfFailed(m_device->CreateCommittedResource(
            &heapProperties,
            D3D12_HEAP_FLAG_NONE,
            &textureDesc,
            // NOTE: though used as RESOLVE_DEST, it is also copied to the CPU
            // start in the copy_source state to align with transition barrier logic in TileUpdateManager
            D3D12_RESOURCE_STATE_COPY_SOURCE,
            nullptr,
            IID_PPV_ARGS(&m_resolvedResource)));
        m_resolvedResource->SetName(L"m_resolvedResource");

        D3D12_RESOURCE_DESC desc;
        desc = m_resolvedResource->GetDesc();
        
        //void GetCopyableFootprints(
        //    [in]            const D3D12_RESOURCE_DESC* pResourceDesc,
        //    [in]            UINT                               FirstSubresource,
        //    [in]            UINT                               NumSubresources,
        //    UINT64                             BaseOffset,
        //    [out, optional] D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts,
        //    [out, optional] UINT* pNumRows,
        //    [out, optional] UINT64* pRowSizeInBytes,
        //    [out, optional] UINT64* pTotalBytes
        //);

        /*UINT */NumSubresources = desc.MipLevels;
        /*std::vector<D3D12_PLACED_SUBRESOURCE_FOOTPRINT>*/ Layouts.resize(NumSubresources);
        /*std::vector<UINT> */NumRows.resize(NumSubresources);
        /*std::vector<UINT64> */RowSizeInBytes.resize(NumSubresources);
        //std::vector<UINT64> TotalBytes(NumSubresources);
        //UINT64 TotalBytes;
        m_device->GetCopyableFootprints(&desc, 0, NumSubresources, 0, Layouts.data(), NumRows.data(), RowSizeInBytes.data(), &TotalBytes);

        std::ostringstream ss;
        ss << "Offset, Format, Width, Height, Depth, RowPitch, NumRows, RowSizeInBytes" << std::endl;
        for (decltype(Layouts.size()) i = 0; i < Layouts.size(); ++i)
        {
            ss << Layouts[i].Offset << ", ";
            ss << Layouts[i].Footprint.Format << ", ";
            ss << Layouts[i].Footprint.Width << ", ";
            ss << Layouts[i].Footprint.Height << ", ";
            ss << Layouts[i].Footprint.Depth << ", ";
            ss << Layouts[i].Footprint.RowPitch << ", ";
            ss << NumRows[i] << ", ";
            ss << RowSizeInBytes[i] << std::endl;
        }
        ss << "TotalBytes: " << TotalBytes << std::endl;
        OutputDebugStringA(ss.str().c_str());
    }

    {
        D3D12_RESOURCE_DESC rd = CD3DX12_RESOURCE_DESC::Buffer(TotalBytes/*m_numTilesWidth * m_numTilesHeight*/);
        const auto resolvedHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK);

        //#if RESOLVE_TO_TEXTURE
                // CopyTextureRegion requires pitch multiple of D3D12_TEXTURE_DATA_PITCH_ALIGNMENT = 256
        UINT pitch = m_numTilesWidth;
        pitch = (pitch + 0x0ff) & ~0x0ff;
        rd.Width = pitch * m_numTilesHeight;
        rd.Width = TotalBytes;
        rd.Width = (rd.Width + 0x0ff) & ~0x0ff;
        //#endif

                //m_resolvedReadback.resize(in_swapChainBufferCount);

                //for (auto& b : m_resolvedReadback)
                //{
        ThrowIfFailed(m_device->CreateCommittedResource(
            &resolvedHeapProperties,
            D3D12_HEAP_FLAG_NONE,
            &rd,
            //#if RESOLVE_TO_TEXTURE
                            // start in the copy_source state to align with transition barrier logic in TileUpdateManager
            D3D12_RESOURCE_STATE_COPY_DEST,
            //#else
            //                D3D12_RESOURCE_STATE_RESOLVE_DEST,
            //#endif
            nullptr,
            IID_PPV_ARGS(&m_resolvedReadback)));
        //}
    }
    
    // Close the command list and execute it to begin the initial GPU setup.
    ThrowIfFailed(m_commandList->Close());
    ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
    m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);

    // Create synchronization objects and wait until assets have been uploaded to the GPU.
    {
        ThrowIfFailed(m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)));
        m_fenceValue = 1;

        // Create an event handle to use for frame synchronization.
        m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
        if (m_fenceEvent == nullptr)
        {
            ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()));
        }

        // Wait for the command list to execute; we are reusing the same command 
        // list in our main loop but for now, we just want to wait for setup to 
        // complete before continuing.
        WaitForPreviousFrame();
    }

    {
        CreateMipMaps(m_device.Get(), m_commandList.Get(), m_texture.Get());
    }
}

// Generate a simple black and white checkerboard texture.
std::vector<UINT8> D3D12HelloTexture::GenerateTextureData()
{
    const UINT rowPitch = TextureWidth * TexturePixelSize;
    const UINT cellPitch = rowPitch >> 3;        // The width of a cell in the checkboard texture.
    const UINT cellHeight = TextureHeight >> 3;    // The height of a cell in the checkerboard texture.
    const UINT textureSize = rowPitch * TextureHeight;

    std::vector<UINT8> data(textureSize);
    UINT8* pData = &data[0];

    for (UINT n = 0; n < textureSize; n += TexturePixelSize)
    {
        UINT x = n % rowPitch;
        UINT y = n / rowPitch;
        UINT i = x / cellPitch;
        UINT j = y / cellHeight;

        if (i == 0 && j == 0)
        {
            pData[n] = 0xff;        // R
            pData[n + 1] = 0x00;    // G
            pData[n + 2] = 0x00;    // B
            pData[n + 3] = 0xff;    // A
        }
        else if (i == 7 && j == 0)
        {
            pData[n] = 0x00;        // R
            pData[n + 1] = 0xff;    // G
            pData[n + 2] = 0x00;    // B
            pData[n + 3] = 0xff;    // A
        }
        else if (i == 0 && j == 7)
        {
            pData[n] = 0x00;        // R
            pData[n + 1] = 0x00;    // G
            pData[n + 2] = 0xff;    // B
            pData[n + 3] = 0xff;    // A
        }
        else if (i == 7 && j == 7)
        {
            pData[n] = 0xff;        // R
            pData[n + 1] = 0x00;    // G
            pData[n + 2] = 0xff;    // B
            pData[n + 3] = 0xff;    // A
        }
        else if (i % 2 == j % 2)
        {
            pData[n] = 0x00;        // R
            pData[n + 1] = 0x00;    // G
            pData[n + 2] = 0x00;    // B
            pData[n + 3] = 0xff;    // A
        }
        else
        {
            pData[n] = 0xff;        // R
            pData[n + 1] = 0xff;    // G
            pData[n + 2] = 0xff;    // B
            pData[n + 3] = 0xff;    // A
        }
    }

    return data;
}

// Update frame-based values.
void D3D12HelloTexture::OnUpdate()
{
    // Start the Dear ImGui frame
    ImGui_ImplDX12_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();

    // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
    if (show_demo_window)
        ImGui::ShowDemoWindow(&show_demo_window);

    // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
    {
        static float f = 0.0f;
        static int counter = 0;

        ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.

        //ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)

        // 定义一个变量来存储当前选择的选项
        //static int m_textureAreaIndex = 0;

        //ImGui::BeginGroup();
        ImGui::Checkbox("Enable sampler feedback", &enable_sampler_feedback);
        //ImGui::SliderInt("Sample mip level", &m_sampledMipLevel, 0, 10);
        
        static const char* items[] = { "1.0", "2.0", "4.0", "8.0", "16.0", "32.0", "64.0", "128.0", "256.0", "512.0", "1024.0" };
        static int selectedItem = 0;

        if (ImGui::Combo("Tex coord", &m_sampledMipLevel, items, IM_ARRAYSIZE(items)))
        {
            // Handle the selected item change here
            // 'selectedItem' now contains the index of the selected item
        }


        ImGui::Text("Texture sample area:");
        // 创建单选按钮
        ImGui::RadioButton("Full", &m_textureAreaIndex, 0);
        ImGui::RadioButton("Top left", &m_textureAreaIndex, 1);
        ImGui::RadioButton("Top right", &m_textureAreaIndex, 2);
        ImGui::RadioButton("Bottom left", &m_textureAreaIndex, 3);
        ImGui::RadioButton("Bottom right", &m_textureAreaIndex, 4);

        ImGui::Text("Texture UV:");

        // uv:
        // top - left : 3, top - right : 4
        // bottom - left : 2, bottom - right : 5

        Vertex* pVtx = nullptr;
        if (m_textureAreaIndex == 0 && m_sampledMipLevel < 11)
        {
            pVtx = &m_triangleVertices[6 * m_sampledMipLevel];
        }
        else
        {
            assert(m_textureAreaIndex >= 1 && m_textureAreaIndex <= 4);
            pVtx = &m_triangleVertices[6 * (m_textureAreaIndex + 10)];
        }

        //Vertex* pVtx = &m_triangleVertices[6 * (m_textureAreaIndex + 10)];
        uv[0] = pVtx[3].uv;
        uv[1] = pVtx[4].uv;
        uv[2] = pVtx[2].uv;
        uv[3] = pVtx[5].uv;

        ImGui::Text("    top-left: (%f, %f)", uv[0].x, uv[0].y);
        ImGui::Text("   top-right: (%f, %f)", uv[1].x, uv[1].y);
        ImGui::Text(" bottom-left: (%f, %f)", uv[2].x, uv[2].y);
        ImGui::Text("bottom-right: (%f, %f)", uv[3].x, uv[3].y);
        //ImGui::Text("Texture UV:");
        //ImGui::Text("Texture UV:");
        //ImGui::Text("Texture UV:");
        //ImGui::EndGroup();
        //ImGui::Text("currentOption = %d", m_textureAreaIndex);

        //static int currentOption2 = 0;

        //// 创建单选按钮
        //ImGui::RadioButton("选项2 1", &currentOption2, 0);
        //ImGui::RadioButton("选项2 2", &currentOption2, 1);
        //ImGui::RadioButton("选项2 3", &currentOption2, 2);

        ImGui::Text("Feedback map data:");
        ImGui::Text(m_feedbackText.c_str());// "00 00 00 00 ff ff ff ff\n00 00 00 00 ff ff ff ff\n00 00 00 00 ff ff ff ff\n00 00 00 00 ff ff ff ff\n");
        //ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
        //ImGui::Checkbox("Another Window", &show_another_window);

        //ImGui::SliderFloat("float", &f, 0.0f, 1.0f);            // Edit 1 float using a slider from 0.0f to 1.0f
        //ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color

        //if (ImGui::Button("Button"))                            // Buttons return true when clicked (most widgets return true when edited/activated)
            counter++;
        //ImGui::SameLine();
        ImGui::Text("frame = %d", counter);

        ImGuiIO& io = ImGui::GetIO();
        ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
        ImGui::End();
    }

    // 3. Show another simple window.
    if (show_another_window)
    {
        ImGui::Begin("Another Window", &show_another_window);   // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
        ImGui::Text("Hello from another window!");
        if (ImGui::Button("Close Me"))
            show_another_window = false;
        ImGui::End();
    }

    // Rendering
    ImGui::Render();
}

// Render the scene.
void D3D12HelloTexture::OnRender()
{
    // Record all the commands we need to render the scene into the command list.
    PopulateCommandList();

    // Execute the command list.
    ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
    m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);

    // Present the frame.
    ThrowIfFailed(m_swapChain->Present(1, 0));

    WaitForPreviousFrame();

    //------------------------------------------------------------------
    // update the refcount of each tile based on feedback
    //------------------------------------------------------------------
    {
        // mapped host feedback buffer
        UINT8* pResolvedDataHeader = nullptr;
        UINT8* pResolvedData = nullptr;
        ID3D12Resource* pResolvedResource = m_resolvedReadback.Get();// m_resources->GetResolvedReadback(feedbackIndex);
        pResolvedResource->Map(0, nullptr, (void**)&pResolvedData);

        pResolvedDataHeader = pResolvedData;

        //std::wstringstream wsstream;
        std::stringstream sstream;
        for (UINT i=0; i<NumSubresources; ++i)
        {
            sstream << "----- mip: " << i << " -----\n";// << std::endl;
            pResolvedData = pResolvedDataHeader + Layouts[i].Offset;
            //wsstream << std::hex << std::setfill(L'0') << std::setw(2);
            //TileReference* pTileRow = m_tileReferences.data();
            for (UINT y = 0; y < Layouts[i].Footprint.Height /*height*/; y++)
            {
                for (UINT x = 0; x < Layouts[i].Footprint.Width /*width*/; x++)
                {
                    // clamp to the maximum we are tracking (not tracking packed mips)
                    //UINT8 desired = std::min(pResolvedData[x], m_maxMip);
                    UINT8 desired = pResolvedData[x];

                    //std::wstring msg = std::to_wstring(desired) + L" ";
                    //OutputDebugStringW(msg.c_str());
                    {
                        if (x > 0)
                        {
                            //wsstream << L' ';
                            sstream << ' ';
                        }
                        //wsstream << std::hex << std::setfill(L'0') << std::setw(2) << desired;
                        sstream << std::hex << std::setfill('0') << std::setw(2) << (int)desired;
                    }
                    //UINT8 initialValue = pTileRow[x];
                    //if (desired != initialValue) { changed = true; }
                    //SetMinMip(initialValue, x, y, desired);
                    //pTileRow[x] = desired;
                } // end loop over x
                //pTileRow += width;
                //wsstream << L'\n';
                sstream << '\n';

                //#if RESOLVE_TO_TEXTURE
                //pResolvedData += (m_numTilesWidth /*width*/ + 0x0ff) & ~0x0ff;
                //UINT va10 = Layouts[i].Footprint.RowPitch & 0xff;
                assert((Layouts[i].Footprint.RowPitch & 0xff) == 0);
                pResolvedData += Layouts[i].Footprint.RowPitch;

                //OutputDebugStringW(L"\n");
    //#else
    //            pResolvedData += width;
    //#endif

            } // end loop over y

        }

        //std::wstring msg = wsstream.str();
        m_feedbackText = sstream.str();
        //OutputDebugStringA(sstream.str().c_str());
        {
            std::wstringstream sstr;
            auto fv = m_fence->GetCompletedValue();
            sstr << L"---------- ";
            sstr << fv;
            sstr << L" ----------\n";
            std::wstring msg = sstr.str();

            //OutputDebugStringW(msg.c_str());
        }

        D3D12_RANGE emptyRange{ 0,0 };
        pResolvedResource->Unmap(0, &emptyRange);
    }
}

void D3D12HelloTexture::OnDestroy()
{
    // Ensure that the GPU is no longer referencing resources that are about to be
    // cleaned up by the destructor.
    WaitForPreviousFrame();

    CloseHandle(m_fenceEvent);

    // Cleanup
    ImGui_ImplDX12_Shutdown();
    ImGui_ImplWin32_Shutdown();
    ImGui::DestroyContext();

    // Before your application exits:
    {
        //ComPtr<ID3D12DebugDevice> DebugDevice;
        HRESULT hr = m_device.As(&DebugDevice);
        if (SUCCEEDED(hr))
        {
            //DebugDevice->ReportLiveDeviceObjects(D3D12_RLDO_SUMMARY | D3D12_RLDO_DETAIL);
        }
    }
}

void D3D12HelloTexture::PopulateCommandList()
{
    // Command list allocators can only be reset when the associated 
    // command lists have finished execution on the GPU; apps should use 
    // fences to determine GPU execution progress.
    ThrowIfFailed(m_commandAllocator->Reset());

    // However, when ExecuteCommandList() is called on a particular command 
    // list, that command list can then be reset at any time and must be before 
    // re-recording.
    ThrowIfFailed(m_commandList->Reset(m_commandAllocator.Get(), nullptr));

    // Set necessary state.
    m_commandList->SetPipelineState(enable_sampler_feedback ? m_pipelineStateFB.Get() : m_pipelineState.Get());
    m_commandList->SetGraphicsRootSignature(enable_sampler_feedback ? m_rootSignatureFB.Get() : m_rootSignature.Get());

    ID3D12DescriptorHeap* ppHeaps[] = { m_srvHeap.Get() };
    m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);

    // clear feedback texture
    {
        CD3DX12_GPU_DESCRIPTOR_HANDLE gpuHandle(m_srvHeap->GetGPUDescriptorHandleForHeapStart(), 1, m_rtvDescriptorSize);
        CD3DX12_CPU_DESCRIPTOR_HANDLE secondHeapCPU(m_feedbackHeap->GetCPUDescriptorHandleForHeapStart());// , 1, m_rtvDescriptorSize);

        // note clear value is ignored when clearing feedback maps
        UINT clearValue[4]{};
        m_commandList->ClearUnorderedAccessViewUint(
            gpuHandle,
            secondHeapCPU,
            m_feedbackResource.Get(),
            clearValue, 0, nullptr);

        m_barrierUavToResolveSrc.push_back(CD3DX12_RESOURCE_BARRIER::Transition(m_feedbackResource.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_RESOLVE_SOURCE));

        // after resolving, transition the opaque resources back to UAV. Transition the resolve destination to copy source for read back on cpu
        m_barrierResolveSrcToUav.push_back(CD3DX12_RESOURCE_BARRIER::Transition(m_feedbackResource.Get(), D3D12_RESOURCE_STATE_RESOLVE_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS));

//#if RESOLVE_TO_TEXTURE
        // resolve to texture incurs a subsequent copy to linear buffer
        m_barrierUavToResolveSrc.push_back(CD3DX12_RESOURCE_BARRIER::Transition(m_resolvedResource.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RESOLVE_DEST));
        m_barrierResolveSrcToUav.push_back(CD3DX12_RESOURCE_BARRIER::Transition(m_resolvedResource.Get(), D3D12_RESOURCE_STATE_RESOLVE_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE));
    }

    m_commandList->SetGraphicsRootDescriptorTable(0, m_srvHeap->GetGPUDescriptorHandleForHeapStart());
    m_commandList->RSSetViewports(1, &m_viewport);
    m_commandList->RSSetScissorRects(1, &m_scissorRect);

    // Indicate that the back buffer will be used as a render target.
    m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));

    CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize);
    m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);

    // Record commands.
    const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
    m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
    m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);
    if (m_textureAreaIndex == 0)
    {
        m_commandList->DrawInstanced(6, 1, 6 * m_sampledMipLevel, 0);
    }
    else
    {
        //
        m_commandList->DrawInstanced(6, 1, 6 * (m_textureAreaIndex + 10), 0);
    }

    {
        // transition all feedback resources UAV->RESOLVE_SOURCE
        // also transition the (non-opaque) resolved resources COPY_SOURCE->RESOLVE_DEST
        m_commandList->ResourceBarrier((UINT)m_barrierUavToResolveSrc.size(), m_barrierUavToResolveSrc.data());
        m_barrierUavToResolveSrc.clear();

        ComPtr<ID3D12GraphicsCommandList1> commandList1;
        ThrowIfFailed(m_commandList.As(&commandList1));


        // resolve the min mip map
        // can resolve directly to a host readable buffer
        for (UINT i = 0; i < NumSubresources; ++i)
        {
            commandList1->ResolveSubresourceRegion(
                m_resolvedResource.Get(),
                i,                   // decode target only has 1 layer (or is a buffer)
                0, 0,
                m_feedbackResource.Get(),
                /*UINT_MAX*/i,            // decode SrcSubresource must be UINT_MAX
                nullptr,             // src rect is not supported for min mip maps
                DXGI_FORMAT_R8_UINT, // decode format must be R8_UINT
                D3D12_RESOLVE_MODE_DECODE_SAMPLER_FEEDBACK
            );
        }

        // transition all feedback resources RESOLVE_SOURCE->UAV
        // also transition the (non-opaque) resolved resources RESOLVE_DEST->COPY_SOURCE
        m_commandList->ResourceBarrier((UINT)m_barrierResolveSrcToUav.size(), m_barrierResolveSrcToUav.data());
        m_barrierResolveSrcToUav.clear();
    }

    {
        ID3D12Resource* pResolvedReadback = m_resolvedReadback.Get();// [in_index] .Get();
        auto srcDesc = m_resolvedResource->GetDesc();

        for (UINT i = 0; i < NumSubresources; ++i)
        {
            D3D12_TEXTURE_COPY_LOCATION srcLocation = CD3DX12_TEXTURE_COPY_LOCATION(m_resolvedResource.Get(), i);
            D3D12_TEXTURE_COPY_LOCATION dstLocation = CD3DX12_TEXTURE_COPY_LOCATION(pResolvedReadback, Layouts[i]);

            m_commandList->CopyTextureRegion(
                &dstLocation,
                0, 0, 0,
                &srcLocation,
                nullptr);
        }
    }

    m_commandList->SetDescriptorHeaps(1, m_imguiSrvHeap.GetAddressOf());
    ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), m_commandList.Get());

    // Indicate that the back buffer will now be used to present.
    m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));

    ThrowIfFailed(m_commandList->Close());
}

void D3D12HelloTexture::WaitForPreviousFrame()
{
    // WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE.
    // This is code implemented as such for simplicity. The D3D12HelloFrameBuffering
    // sample illustrates how to use fences for efficient resource usage and to
    // maximize GPU utilization.

    // Signal and increment the fence value.
    const UINT64 fence = m_fenceValue;
    ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), fence));
    m_fenceValue++;

    // Wait until the previous frame is finished.
    if (m_fence->GetCompletedValue() < fence)
    {
        ThrowIfFailed(m_fence->SetEventOnCompletion(fence, m_fenceEvent));
        WaitForSingleObject(m_fenceEvent, INFINITE);
    }

    m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
}

//_mipMapComputeShader is an ID3DBlob of the compiled mipmap compute shader
void D3D12HelloTexture::CreateMipMaps(ID3D12Device* _device, ID3D12GraphicsCommandList* commandList, ID3D12Resource* pTextureResource)
{
    //Union used for shader constants
    struct DWParam
    {
        DWParam(FLOAT f) : Float(f) {}
        DWParam(UINT u) : Uint(u) {}

        void operator= (FLOAT f) { Float = f; }
        void operator= (UINT u) { Uint = u; }

        union
        {
            FLOAT Float;
            UINT Uint;
        };
    };

    commandList->Reset(m_commandAllocator.Get(), nullptr);

    //Calculate heap size
    UINT requiredHeapSize = 0;
    UINT mipLevels = 0;
    UINT textureWidth = 0;
    UINT textureHeight = 0;
    DXGI_FORMAT textureFormat;
    //_mipMapTextures->Enumerate<D3D12Texture>([&](D3D12Texture* texture, size_t index, bool& stop) {
    //    if (texture->mipMaps > 1)
    //        requiredHeapSize += texture->mipMaps - 1;
    //    });

    ////No heap size, means that there was either no texture or none that requires any mipmaps
    //if (requiredHeapSize == 0)
    //{
    //    _mipMapTextures->RemoveAllObjects();
    //    return;
    //}


    // 假设您已经创建了一个 ID3D12Resource 对象，例如纹理资源
    //ID3D12Resource* pTextureResource = /* your resource here */;

    // 获取资源描述
    D3D12_RESOURCE_DESC resourceDesc = pTextureResource->GetDesc();

    // 检查是否为纹理
    if (resourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D)
    {
        // 计算 Mipmaps 的数量
        /*UINT*/ mipLevels = resourceDesc.MipLevels;

        requiredHeapSize = mipLevels - 1;
        textureWidth = static_cast<UINT>(resourceDesc.Width);
        textureHeight = resourceDesc.Height;
        textureFormat = resourceDesc.Format;

        // 现在您可以使用 mipLevels 变量，例如打印出 Mipmaps 数量
        //printf("Mipmaps 数量：%u\n", mipLevels);
    }
    else
    {
        //printf("这不是一个 2D 纹理资源。\n");
    }

    if (requiredHeapSize == 0)
    {
        //_mipMapTextures->RemoveAllObjects();
        return;
    }




    //The compute shader expects 2 floats, the source texture and the destination texture
    CD3DX12_DESCRIPTOR_RANGE srvCbvRanges[2];
    CD3DX12_ROOT_PARAMETER rootParameters[3];
    srvCbvRanges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0);
    srvCbvRanges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0, 0);
    rootParameters[0].InitAsConstants(2, 0);
    rootParameters[1].InitAsDescriptorTable(1, &srvCbvRanges[0]);
    rootParameters[2].InitAsDescriptorTable(1, &srvCbvRanges[1]);

    //Static sampler used to get the linearly interpolated color for the mipmaps
    D3D12_STATIC_SAMPLER_DESC samplerDesc = {};
    samplerDesc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
    samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    samplerDesc.MipLODBias = 0.0f;
    samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
    samplerDesc.MinLOD = 0.0f;
    samplerDesc.MaxLOD = D3D12_FLOAT32_MAX;
    samplerDesc.MaxAnisotropy = 0;
    samplerDesc.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
    samplerDesc.ShaderRegister = 0;
    samplerDesc.RegisterSpace = 0;
    samplerDesc.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;

    //Create the root signature for the mipmap compute shader from the parameters and sampler above
    ID3DBlob* signature;
    ID3DBlob* error;
    CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
    rootSignatureDesc.Init(_countof(rootParameters), rootParameters, 1, &samplerDesc, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
    HRESULT hr = D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);
    assert(SUCCEEDED(hr));
    ComPtr<ID3D12RootSignature> mipMapRootSignature;
    hr = _device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(mipMapRootSignature.GetAddressOf()));
    assert(SUCCEEDED(hr));

    //Create the descriptor heap with layout: source texture - destination texture
    D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
    heapDesc.NumDescriptors = 2 * requiredHeapSize;
    heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    ComPtr<ID3D12DescriptorHeap> descriptorHeap;
    hr = _device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(descriptorHeap.GetAddressOf()));
    assert(SUCCEEDED(hr));
    UINT descriptorSize = _device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    ComPtr<IDxcLibrary> pLibrary;
    ComPtr<IDxcCompiler> pCompiler;
    ComPtr<IDxcBlobEncoding> pSource;

    //ComPtr<IDxcBlob> vertexShader;
    //ComPtr<IDxcBlob> pixelShader;
    //ComPtr<IDxcBlob> pixelShaderFB;
    ComPtr<IDxcBlob> computeShader;

    // 初始化 dxc
    DxcCreateInstance(CLSID_DxcLibrary, __uuidof(IDxcLibrary), (void**)&pLibrary);
    DxcCreateInstance(CLSID_DxcCompiler, __uuidof(IDxcCompiler), (void**)&pCompiler);

    // 从文件中读取 HLSL 代码
    std::ifstream mipmapsFile(GetAssetFullPath(L"mipmaps.hlsl"));
    if (mipmapsFile.fail())
    {
        OutputDebugStringA("Error: Failed to open file 'mipmaps.hlsl'\n");
        ThrowIfFailed(E_FAIL); // 或者你可以选择其他的错误处理方式
    }
    std::string mipmapsCode((std::istreambuf_iterator<char>(mipmapsFile)), std::istreambuf_iterator<char>());
    pLibrary->CreateBlobWithEncodingFromPinned(mipmapsCode.c_str(), mipmapsCode.size(), 0, &pSource);

    // 编译 HLSL 代码
    ComPtr<IDxcOperationResult> pResult;
    pCompiler->Compile(pSource.Get(), L"mipmaps.hlsl", L"GenerateMipMaps", L"cs_6_5", nullptr, 0, nullptr, 0, nullptr, &pResult);

    // 检查编译结果
    HRESULT hrCompilation;
    pResult->GetStatus(&hrCompilation);
    if (SUCCEEDED(hrCompilation)) {
        // 编译成功，获取编译后的字节码
        pResult->GetResult(&computeShader);
    }
    else {
        // 编译失败，获取错误信息
        IDxcBlobEncoding* pError;
        pResult->GetErrorBuffer(&pError);
        OutputBlob(pError);
        ThrowIfFailed(E_FAIL);
    }

    // 释放资源
    pSource.Reset();
    pCompiler.Reset();
    pLibrary.Reset();


    //Create pipeline state object for the compute shader using the root signature.
    D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
    psoDesc.pRootSignature = mipMapRootSignature.Get();
    psoDesc.CS = { reinterpret_cast<UINT8*>(computeShader->GetBufferPointer()), computeShader->GetBufferSize() };
    ComPtr<ID3D12PipelineState> psoMipMaps;
    hr = _device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(psoMipMaps.GetAddressOf()));
    assert(SUCCEEDED(hr));

    //Prepare the shader resource view description for the source texture
    D3D12_SHADER_RESOURCE_VIEW_DESC srcTextureSRVDesc = {};
    srcTextureSRVDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srcTextureSRVDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;

    //Prepare the unordered access view description for the destination texture
    D3D12_UNORDERED_ACCESS_VIEW_DESC destTextureUAVDesc = {};
    destTextureUAVDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;

    //Get a new empty command list in recording state
    //ID3D12GraphicsCommandList* commandList = GetNewCommandList();

    //Set root signature, pso and descriptor heap
    commandList->SetComputeRootSignature(mipMapRootSignature.Get());
    commandList->SetPipelineState(psoMipMaps.Get());
    ID3D12DescriptorHeap* heaps[1] = { descriptorHeap.Get() };
    commandList->SetDescriptorHeaps(1, heaps);

    //CPU handle for the first descriptor on the descriptor heap, used to fill the heap
    CD3DX12_CPU_DESCRIPTOR_HANDLE currentCPUHandle(descriptorHeap->GetCPUDescriptorHandleForHeapStart(), 0, descriptorSize);

    //GPU handle for the first descriptor on the descriptor heap, used to initialize the descriptor tables
    CD3DX12_GPU_DESCRIPTOR_HANDLE currentGPUHandle(descriptorHeap->GetGPUDescriptorHandleForHeapStart(), 0, descriptorSize);

    // _mipMapTextures->Enumerate<D3D12Texture>([&](D3D12Texture* texture, size_t index, bool& stop) {
        //Skip textures without mipmaps
        //if (texture->mipMaps <= 1)
        //    return;

        //Transition from pixel shader resource to unordered access
        commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(/*texture->_resource*/pTextureResource, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS));

        //Loop through the mipmaps copying from the bigger mipmap to the smaller one with downsampling in a compute shader
        for (uint32_t TopMip = 0; TopMip < /*texture->mipMaps*/mipLevels - 1; TopMip++)
        {
            commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(/*texture->_resource*/pTextureResource, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, TopMip));

            //Get mipmap dimensions
            uint32_t dstWidth = std::max(/*texture->width*/ textureWidth >> (TopMip + 1), 1u);
            uint32_t dstHeight = std::max(/*texture->height*/ textureHeight >> (TopMip + 1), 1u);

            //Create shader resource view for the source texture in the descriptor heap
            srcTextureSRVDesc.Format = /*texture->_format*/ textureFormat;
            srcTextureSRVDesc.Texture2D.MipLevels = 1;
            srcTextureSRVDesc.Texture2D.MostDetailedMip = TopMip;
            _device->CreateShaderResourceView(/*texture->_resource*/pTextureResource, &srcTextureSRVDesc, currentCPUHandle);
            currentCPUHandle.Offset(1, descriptorSize);

            //Create unordered access view for the destination texture in the descriptor heap
            destTextureUAVDesc.Format = /*texture->_format*/ textureFormat;
            destTextureUAVDesc.Texture2D.MipSlice = TopMip + 1;
            _device->CreateUnorderedAccessView(/*texture->_resource*/pTextureResource, nullptr, &destTextureUAVDesc, currentCPUHandle);
            currentCPUHandle.Offset(1, descriptorSize);

            //Pass the destination texture pixel size to the shader as constants
            commandList->SetComputeRoot32BitConstant(0, DWParam(1.0f / dstWidth).Uint, 0);
            commandList->SetComputeRoot32BitConstant(0, DWParam(1.0f / dstHeight).Uint, 1);

            //Pass the source and destination texture views to the shader via descriptor tables
            commandList->SetComputeRootDescriptorTable(1, currentGPUHandle);
            currentGPUHandle.Offset(1, descriptorSize);
            commandList->SetComputeRootDescriptorTable(2, currentGPUHandle);
            currentGPUHandle.Offset(1, descriptorSize);

            //Dispatch the compute shader with one thread per 8x8 pixels
            commandList->Dispatch(std::max(dstWidth / 8, 1u), std::max(dstHeight / 8, 1u), 1);

            //Wait for all accesses to the destination texture UAV to be finished before generating the next mipmap, as it will be the source texture for the next mipmap
            commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(/*texture->_resource*/pTextureResource, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, TopMip));
            //commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::UAV(/*texture->_resource*/pTextureResource));
        }

        //When done with the texture, transition it's state back to be a pixel shader resource
        commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(/*texture->_resource*/pTextureResource, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
        // });

    //Close and submit the command list
    commandList->Close();
    //SubmitCommandList(commandList);

    // Execute the command list.
    ID3D12CommandList* ppCommandLists[] = { commandList };
    m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);

    // Present the frame.
    //ThrowIfFailed(m_swapChain->Present(1, 0));

    WaitForPreviousFrame();

    //_mipMapTextures->RemoveAllObjects();
}
